using System;using System.Collections.Generic;
using System.Linq;


using Styx;
using Styx.CommonBot;
using Styx.CommonBot.Routines;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;


using Action = Styx.TreeSharp.Action;


namespace Armed
{
    internal class Afflicted : CombatRoutine
    {
        #region Generic Wrapper Stuff
        private Composite _buffs;
        private Composite _combat;
        public override string Name { get { return "Afflicted"; } }


        public override WoWClass Class { get { return WoWClass.Warlock; } }


        public override Composite CombatBehavior
        {
            get { return _combat ?? (_combat = CreateCombat()); }
        }


        private WoWUnit GetTargetMissingDebuff(WoWUnit near, string debuff, float distance = -1f)
        {
            return UnfriendlyUnits.FirstOrDefault(u => u.Location.Distance(near.Location) <= distance && !u.HasAura(debuff, 1, true));
        }


        private int EnemiesNearTarget(float range)
        {
            var target = StyxWoW.Me.CurrentTarget;
            if (target == null)
                return 0;
            var tarLoc = target.Location;
            return UnfriendlyUnits.Count(u => u.Guid != target.Guid && u.Location.Distance(tarLoc) <= range);
        }


        private IEnumerable<WoWUnit> UnfriendlyUnits
        {
            get
            {
                return
                    ObjectManager.GetObjectsOfType<WoWUnit>(true, false).Where(u => !u.IsDead && u.CanSelect && u.Attackable && !u.IsFriendly);
            }
        }


        private void UseTrinkets()
        {
            WoWItem firstTrinket = StyxWoW.Me.Inventory.Equipped.Trinket1;
            WoWItem secondTrinket = StyxWoW.Me.Inventory.Equipped.Trinket2;


            if (firstTrinket != null && CanUseEquippedItem(firstTrinket))
                firstTrinket.Use();


            if (secondTrinket != null && CanUseEquippedItem(secondTrinket))
                secondTrinket.Use();
        }


        private static bool CanUseEquippedItem(WoWItem item)
        {
            // Check for engineering tinkers!
            var itemSpell = Lua.GetReturnVal<string>("return GetItemSpell(" + item.Entry + ")", 0);
            if (string.IsNullOrEmpty(itemSpell))
                return false;


            return item.Usable && item.Cooldown <= 0;
        }


        private Composite Cast(string spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCheck = false)
        {
            return
                new Decorator(
                    ret => (((reqs != null && reqs(ret)) || (reqs == null)) && SpellManager.CanCast(spell)) || ignoreCheck,
                    new Action(ret => SpellManager.Cast(spell, (onTarget != null ? onTarget(ret) : null))));
        }
        private Composite Debuff(string spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, int msLeft = 0, bool ignoreCheck = false)
        {
            return
                new Decorator(
                    ret => (((reqs != null && reqs(ret)) || (reqs == null)) && (ignoreCheck || SpellManager.CanCast(spell))) && !(onTarget != null ? onTarget(ret) : StyxWoW.Me.CurrentTarget).HasAura(spell, 0, true, msLeft),
                    new Action(ret =>
                        {
                            var castingSpell = StyxWoW.Me.CastingSpell;
                            if (castingSpell != null && castingSpell.Name != spell)
                                Lua.DoString("SpellStopCasting()");
                            SpellManager.Cast(spell, (onTarget != null ? onTarget(ret) : null));
                        }));
        }


        private Composite Apply(string spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null,
            int msLeft = 0, bool ignoreCanCast = false)
        {
            return new Decorator(ret =>
                {
                    if (reqs != null && !reqs(ret))
                        return false;


                    var target = onTarget != null ? onTarget(ret) : StyxWoW.Me.CurrentTarget;


                    if (!ignoreCanCast &&
                        !SpellManager.CanCast(spell, target))
                        return false;


                    if (!target.HasAura(spell, 1, true, msLeft))
                        return true;


                    return false;
                },


                new Action(ret =>
                {
                    var castingSpell = StyxWoW.Me.CastingSpell;
                    if (castingSpell != null && castingSpell.Name != spell)
                        Lua.DoString("SpellStopCasting()");
                    SpellManager.Cast(spell, (onTarget != null ? onTarget(ret) : null));
                }));
        }


        public static TimeSpan GetSpellCooldown(string spell)
        {
            SpellFindResults results;
            if (SpellManager.FindSpell(spell, out results))
            {
                if (results.Override != null)
                    return results.Override.CooldownTimeLeft;
                return results.Original.CooldownTimeLeft;
            }


            return TimeSpan.MaxValue;
        }


        private delegate T Selection<out T>(object context);


        #endregion


        #region Actual Logic


        public Composite CreateCombat()
        {
            return new PrioritySelector(
                // Free haste. Use it on CD.
                Cast("Dark Soul: Misery"),
                Cast("Life Tap", ret=>StyxWoW.Me.ManaPercent <= 35 && StyxWoW.Me.HealthPercent >= 60),


                CreateAoeCombat(),


                // Debuff CoE, refresh when <= 1500ms left on the debuff.
                Debuff("Curse of the Elements", msLeft:2000),
                Debuff("Haunt", ret=>StyxWoW.Me.CurrentSoulShards >= 1, msLeft:2500, ignoreCheck:true),
                Debuff("Agony", msLeft: 2000, ignoreCheck: true),
                Debuff("Corruption", msLeft: 2000, ignoreCheck: true),
                Debuff("Unstable Affliction", msLeft: 2500, ignoreCheck: true),
                
                new Decorator(ret=>!StyxWoW.Me.IsMoving,
                    new PrioritySelector(
                        Cast("Drain Soul", ret=>StyxWoW.Me.CurrentTarget.HealthPercent < 20),
                        Cast("Malefic Grasp")
                        )),


                Cast("Fel Flame", ret=>!StyxWoW.Me.IsChanneling)
                );
        }


        public Composite CreateAoeCombat()
        {
            // More than 4 mobs near our current target, start the AOE rotation.
            return new Decorator(ret => EnemiesNearTarget(15) >= 4,
                new PrioritySelector(
                    Cast("Soulburn", ret=>!StyxWoW.Me.HasAura("Soulburn")),
                    Cast("Curse of the Elements", null, ret => GetTargetMissingDebuff(StyxWoW.Me.CurrentTarget, "Curse of the Elements", 15f)),
                    Cast("Seed of Corruption")
                    ));
        }




        #endregion
    }




    public static class Extensions
    {
        public static bool HasAura(this WoWUnit unit, string aura, int stacks, bool isMyAura = false, int msLeft = 0)
        {
            var result = unit.GetAuraByName(aura);
            if (result == null)
                return false;


            if (isMyAura && result.CreatorGuid != StyxWoW.Me.Guid)
                return false;


            if (result.TimeLeft.TotalMilliseconds > msLeft)
                return result.StackCount >= stacks;


            return false;
        }
    }
}